'''
Brilliant - Hunger Games 2012

Submitted by: Fredrik Tomalin

Strategy: This program uses different strategies based on whichever gives it a proper balance of
            maintaining my reputation to be near the mean reputation and maximizing gain (or
            techinically, minimizing loss). Having a reputation near the mean reputation assures
            that my reputation will approach the "ideal" reputation, the one which the reputations
            of the remaining players will eventually converge to as equilibrium is reached.
            Needless to say, maximizing the gain (or minimizing the loss) will certainly make
            your program stay longer in the game.
'''

import numpy as np

class Player:
    def __init__(self):
        self.roundStrategy = 1
        self.minHunt = 0;
        self.maxHunt = 0;
        self.generalHuntProbability = 0;
        self.huntDecisions = []
        self.strategyOneDecisions = []
        self.strategyTwoDecisions = []
        self.strategyThreeDecisions = []


    #********************
    #supporting functions for hunt_choices
    def setGlobalVariables(self):
        self.activePlayers = len(self.player_reputations) + 1
        self.meanValue = np.mean(self.player_reputations)
        self.standardDeviation = np.std(self.player_reputations)
        self.minReputation = min(self.player_reputations)
        self.maxReputation = max(self.player_reputations)
        self.huntActiveness = 3
        self.huntDecisions = []
        self.strategyOneDecisions = []
        self.strategyTwoDecisions = []
        self.strategyThreeDecisions = []


    #********************
    #different strategies to be used

    #this strategy allows my program to hunt with those whose reputation is  above the
    # average reputations gained (for the previous round only) and slack with all others
    def useStrategyOne(self, pairReputation):
        if (pairReputation >= self.generalHuntProbability):
            return 'h'
        else:
            return 's'


    #this strategy determines which reputation will the program pair most likely hunt given
    # his reputation (ie. if he has reputation of 0.4, he is likely to hunt with those in the
    # upper 40% when sorted according to reputations). my program will hunt with those whose
    # "hunting range" includes my reputation but will slack with those who seems to be certain
    # to hunt (ie. very high reputation). my program then will slack with all others.
    def useStrategyTwo(self, pairReputation):
        huntThreshold = round(pairReputation * (self.activePlayers-2))
        sortedReputations = np.sort(self.player_reputations)[::-1]
        huntReputationThreshold = sortedReputations[huntThreshold];

        if (self.current_reputation > (huntReputationThreshold + (0.6*(1-huntReputationThreshold)))):
            return 's'
        elif (self.current_reputation > huntReputationThreshold):
            return 'h'
        else:
            return 's'


    #this strategy determines which range should my program hunt with. the bounds will be decided
    # based on the location of my reputation on the reputation distribution. this also assures that
    # my reputation is near the mean and will adjust hunt activeness as necessary when my
    # reputation drifts from the mean.
    def useStrategyThree(self, pairReputation):
        if ((pairReputation >= self.minHunt) and (pairReputation <= self.maxHunt)):
            return 'h'
        else:
            return 's'


    def setHuntActiveness(self):
        if (self.current_reputation > (self.meanValue - (0.25*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness - 1 #slack more
        if (self.current_reputation > (self.meanValue - (0.75*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness - 1 #slack even more
        if (self.current_reputation > (self.meanValue - (1.25*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness - 1 #always slack
        if (self.current_reputation < (self.meanValue + (0.25*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness + 1; #hunt more
        if (self.current_reputation < (self.meanValue + (0.75*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness + 1; #hunt even more
        if (self.current_reputation < (self.meanValue + (1.25*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness + 1; #hunt always
        if ((self.m/self.activePlayers) > (self.meanValue + (0.5*self.standardDeviation))):
            self.huntActiveness = self.huntActiveness - 1
        if ((self.m/self.activePlayers) > (self.meanValue + (self.standardDeviation))):
            self.huntActiveness = self.huntActiveness - 1


    def setHuntingBounds (self):
        if (self.huntActiveness < 1):
            self.minHunt = 0
            self.maxHunt = 0
        elif (self.huntActiveness < 2):
            self.minHunt = self.meanValue
            self.maxHunt = self.meanValue + (0.25*self.standardDeviation)
        elif (self.huntActiveness < 3):
            self.minHunt = self.meanValue - (0.25*self.standardDeviation)
            self.maxHunt = self.meanValue + (0.25*self.standardDeviation)
        elif (self.huntActiveness < 4):
            self.minHunt = self.meanValue - (0.25*self.standardDeviation)
            self.maxHunt = self.meanValue + (0.75*self.standardDeviation)
        elif (self.huntActiveness < 5):
            self.minHunt = self.meanValue - (0.75*self.standardDeviation)
            self.maxHunt = self.meanValue + (1.25*self.standardDeviation)
        elif (self.huntActiveness < 6):
            self.minHunt = self.meanValue - (1.25*self.standardDeviation)
            self.maxHunt = 1
        elif (self.huntActiveness < 7):
            self.minHunt = 0
            self.maxHunt = 1


    #stores possible decisions made by each strategy which will be used at the end of the round
    # to determine the best performing strategy
    def populateDecisionArrays(self):
        counter = 0;
        while (counter < (self.activePlayers - 1)):
            self.strategyOneDecisions.append(self.useStrategyOne(self.player_reputations[counter]))
            self.strategyTwoDecisions.append(self.useStrategyTwo(self.player_reputations[counter]))
            self.strategyThreeDecisions.append(self.useStrategyThree(self.player_reputations[counter]))
            counter = counter + 1

        if (self.round_number < 50):
            self.huntDecisions = self.strategyOneDecisions
        else:
            if (3 == self.huntActiveness):
                if (1 == self.roundStrategy):
                    self.huntDecisions = self.strategyOneDecisions
                elif (2 == self.roundStrategy):
                    self.huntDecisions = self.strategyTwoDecisions
                else:
                    self.huntDecisions = self.strategyThreeDecisions
            else:
                self.huntDecisions = self.strategyThreeDecisions



    #********************
    #support functions for hunt_outcomes
    def determineScore(self, earning, decision):
        if (earning >= 0):
            if ('h' == decision):
                return 3
            else:
                return 4
        else:
            if ('h' == decision):
                return 0
            else:
                return 1

    def setRoundStrategy(self):
        counter = 0
        scoreOne = 0
        scoreTwo = 0
        scoreThree = 0
        while (counter < (self.activePlayers - 1)):
            
            scoreOne = scoreOne + self.determineScore(self.food_earnings[counter],self.strategyOneDecisions[counter])
            scoreTwo = scoreTwo + self.determineScore(self.food_earnings[counter],self.strategyTwoDecisions[counter])
            scoreThree = scoreThree + self.determineScore(self.food_earnings[counter],self.strategyThreeDecisions[counter])
            counter = counter + 1

        if ((scoreOne > scoreTwo) and (scoreOne > scoreThree)):
            self.roundStrategy = 1
        elif (scoreTwo > scoreThree):
            self.roundStrategy = 2
        else:
            self.roundStrategy = 3



    #********************
    #required methods
    def hunt_choices(self, round_number, current_food, current_reputation, m, player_reputations):
        self.round_number = round_number
        self.current_food = current_food
        self.current_reputation = current_reputation
        self.m = m
        self.player_reputations = player_reputations

        self.setGlobalVariables()
        self.setHuntActiveness()
        self.setHuntingBounds()
        self.populateDecisionArrays()

        hunt_decisions = []
        hunt_decisions = self.huntDecisions
        return hunt_decisions


    def hunt_outcomes(self, food_earnings):
        self.food_earnings = food_earnings
        self.setRoundStrategy()


    def round_end(self, award, m, number_hunters):
        self.generalHuntProbability = number_hunters/self.activePlayers

#end of code
